/*
This is part of Screenbar, which is based in part on the Litestep
shell source code.

Copyright (C) 2008-2009 Jim Babcock
Copyright (C) 1997-2009 The LiteStep Development Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "stdafx.h"

void ScreenbarWindow::setTransparencyMode(WindowTransparency newMode)
{
	if(newMode == currentTransparency)
		return;
	
	long windowExStyle = GetWindowLong(window, GWL_EXSTYLE);
	if(alwaysOnTop)
		windowExStyle |= WS_EX_TOPMOST;
	
	// Clean up any data used by the old transparency mode, leaving the window
	// non-layered (opaque)
	switch(currentTransparency)
	{
		case colorKeyed:
			SetWindowLong(window, GWL_EXSTYLE, windowExStyle&~WS_EX_LAYERED);
			break;
		case alphaOverlay:
			SetWindowLong(window, GWL_EXSTYLE, windowExStyle&~WS_EX_LAYERED);
			break;
		case opaque:
			break;
	}
	
	// Switch to the new transparency mode
	switch(newMode)
	{
		case colorKeyed:
			SetWindowLong(window, GWL_EXSTYLE, windowExStyle|WS_EX_LAYERED);
			SetLayeredWindowAttributes(window, RGB(255,0,255), opacity, LWA_COLORKEY|LWA_ALPHA);
			break;
		case alphaOverlay:
			SetWindowLong(window, GWL_EXSTYLE, windowExStyle|WS_EX_LAYERED);
			break;
		case opaque:
			SetWindowLong(window, GWL_EXSTYLE, windowExStyle&~WS_EX_LAYERED);
			break;
	}
	
	currentTransparency = newMode;
	forceRedraw();
}

void ScreenbarWindow::clearBackBuffer()
{
	if(transparency == alphaOverlay)
	{
		memset(backBuffer->getBits(), 0, visibleRect.width*visibleRect.height*4);
	}
	else
	{
		RECT fillRect = {0,0,visibleRect.width, visibleRect.height};
		
		HBRUSH clearBrush = CreateSolidBrush(fillColor);
		FillRect(backBuffer->getDC(), &fillRect, clearBrush);
		DeleteObject(clearBrush);
		
		// HACK: Set alpha=0xff on all pixels (not using gdi) because FillRect
		// isn't aware of alpha channels
		unsigned char *bits = (unsigned char*)backBuffer->getBits();
		for(int ii=0; ii<visibleRect.width*visibleRect.height; ii++)
			bits[3+4*ii] = 0xff;
	}
}

void ScreenbarWindow::flipBackBuffer(HDC windowDrawContext)
{
	BackBuffer *displayBuf = backBuffer;
	bool transferAnimation = false;
	
	if(animationState.animation)
	{
		animationState.updateTime();
		if(animationState.animation->requiresTransfer())
			transferAnimation = true;
	}
	
	if(transferAnimation)
	{
		if(transparency==opaque) {
			// HACK: Set alpha=0xff on all pixels (not using gdi) because FillRect
			// isn't aware of alpha channels
			unsigned char *bits = (unsigned char*)backBuffer->getBits();
			for(int ii=0; ii<visibleRect.width*visibleRect.height; ii++)
				bits[3+4*ii] = 0xff;
		}
		
		displayBuf = new BackBuffer(visibleRect.width, visibleRect.height);
		animationState.animation->apply(&animationState,
			backBuffer->getDC(), displayBuf->getDC(), 0, 0, visibleRect.width, visibleRect.height);
	}
	
	if(currentTransparency == alphaOverlay)
	{
		HDC screenDC = GetDC(0);
		
		int windowOpacity = opacity;
		if(animationState.animation)
			windowOpacity *= animationState.animation->getAlpha(&animationState);
		
		BLENDFUNCTION blendFunc;
		blendFunc.BlendOp = AC_SRC_OVER;
		blendFunc.BlendFlags = 0;
		blendFunc.SourceConstantAlpha = windowOpacity;
		blendFunc.AlphaFormat = AC_SRC_ALPHA;
		
		POINT windowTopLeft = {visibleRect.left, visibleRect.top};
		SIZE windowSize = {visibleRect.width, visibleRect.height};
		POINT sourcePos = {0,0};
		
		// Work around (0,0) overlay bug in Windows
		if(visibleRect.left==0 && visibleRect.top==0) {
			windowTopLeft.x = 1;
		}
		
		if(!UpdateLayeredWindow(
			window,
			screenDC, &windowTopLeft, &windowSize,
			displayBuf->getDC(), &sourcePos,
			RGB(255,0,255),
			&blendFunc, ULW_ALPHA|ULW_COLORKEY))
		{
			trace << "UpdateLayeredWindow failed: " << getWindowsErr() << "\n";
		}
		
		ReleaseDC(0, screenDC);
	}
	else
	{
		if(currentTransparency == colorKeyed) {
			int windowOpacity = opacity;
			if(animationState.animation)
				windowOpacity *= animationState.animation->getAlpha(&animationState);
			SetLayeredWindowAttributes(window, RGB(255,0,255), windowOpacity, LWA_COLORKEY|LWA_ALPHA);
		}
		
		BitBlt(windowDrawContext, 0, 0, visibleRect.width, visibleRect.height, displayBuf->getDC(), 0, 0, SRCCOPY);
	}
	
	if(transferAnimation)
		delete displayBuf;
}

